/**
 * 
 */
package com.googlecode.lohr.parser.combinators.matcher;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.googlecode.lohr.parser.combinators.MatchResults;
import com.googlecode.lohr.parser.combinators.Parsers;
import com.googlecode.lohr.parser.combinators.node.AbstractNode;
import com.googlecode.lohr.parser.combinators.node.SequenceNode;

public class SequenceMatcher extends Matcher
{
	private final Matcher[] matchers;

	public SequenceMatcher(Matcher[] matchers)
	{
		this.matchers = matchers;
	}

	@Override
	public MatchResults match(MatchContext ctx, int start) 
	{
		if (matchers.length == 0) 
			return new MatchResults(Arrays.<AbstractNode>asList(new SequenceNode(this, start, start)));

		final List<AbstractNode> combinedResults = new ArrayList<AbstractNode>();

		MatchResults results= ctx.doMatch(matchers[0], start);
		if (!results.success())
			return results;

		MatchResults err= null;
		for (final AbstractNode result : results.matches)
		{
			MatchResults subResults= ctx.doMatch(Parsers.seq(Parsers.tail(matchers)), result.getEnd());
			if (!subResults.success() && err == null)
				err= subResults;
			for (final AbstractNode subResult : subResults.matches)
			{
				final AbstractNode node = new SequenceNode(this, start, subResult.getEnd());
				ArrayList<AbstractNode> children= new ArrayList<AbstractNode>();
				children.add(result);
				children.addAll(subResult.getChildren());
				node.setChildren(children);

				combinedResults.add(node);
			}
		}

		if (combinedResults.isEmpty() && err != null)
			return err;

		return new MatchResults(combinedResults);
	}
}